Purchase Flows

Begin only after you have successfully requested a quote and stored the returned quoteId (and optionally premiumSummary.quoteLinkUrl).

Overview

Two primary ways to complete a purchase after quoting:

  1. Quote Engine Redirect – Use the quoteLinkUrl to send the user to the hosted Embrace Quote Engine experience where they can finalize purchase.
  2. Stripe Embedded Checkout – Stay on your site, collect payment using Stripe Elements, then call the Embrace Stripe purchase endpoint.

Choose based on UX preference and integration depth. Both start from the same quote response.


Redirect (Quote Engine) Flow

1. Obtain quoteLinkUrl

Already covered in the quote request guide: ensure you capture premiumSummary.quoteLinkUrl from the quote response.

2. (Optional) Add Pets Incrementally

If a pet was omitted initially, you may add it using: POST /v2/quotes/fullquote/{quoteId}/pet. See Add a Pet.

3. Redirect User

Redirect the browser to the quoteLinkUrl when the customer chooses to continue. Use a normal window.location.href = quoteLinkUrl; navigation. Consider preserving UTM analytics separately if needed.


Stripe Purchase Flow

The embedded flow keeps the customer on your site while you orchestrate payment collection via Stripe.

1. Checkout Request

Call the Embrace Checkout endpoint to retrieve Stripe keys needed to render a payment form.

Endpoint reference: Checkout Endpoint

Front-End (checkout request)

let publishableKey;
let clientSecret;

async function startCheckout(quoteId){
  const resp = await fetch('/checkout', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ quoteId })
  });
  const data = await resp.json();
  if(data.stripePublishableKey && data.stripeSetupIntentClientSecret){
    publishableKey = data.stripePublishableKey;
    clientSecret = data.stripeSetupIntentClientSecret;
  } else {
    throw new Error('Checkout response missing Stripe keys');
  }
}

Backend Example

app.MapPost("/checkout", async (HttpRequest request) =>
{
    using var reader = new StreamReader(request.Body, Encoding.UTF8);
    var rawBody = await reader.ReadToEndAsync();
    if (string.IsNullOrWhiteSpace(rawBody)) return Results.BadRequest(new { error = "Request body is empty" });

    string? quoteId = null;
    try
    {
        using var doc = JsonDocument.Parse(rawBody);
        if (doc.RootElement.TryGetProperty("quoteId", out var q)) quoteId = q.GetString();
    }
    catch { }

    if (string.IsNullOrWhiteSpace(quoteId)) return Results.BadRequest(new { error = "quoteId field is required" });

    var upstream = new HttpRequestMessage(HttpMethod.Post, $"https://[embrace-test-endpoint]/v2/quotes/{quoteId}/checkout")
    {
        Content = new StringContent(rawBody, Encoding.UTF8, "application/json")
    };
    upstream.Headers.Add("epi-apim-subscription-key", embraceApiKey);

    var upstreamResp = await httpClient.SendAsync(upstream);
    var body = await upstreamResp.Content.ReadAsStringAsync();
    return Results.Content(body, "application/json", upstreamResp.StatusCode);
});

2. Initialize Stripe Elements

Use the publishable key + client secret to render the payment form.

let stripe, elements, paymentMethodId;

function initStripe(){
  stripe = Stripe(publishableKey);
  const appearance = { theme: 'stripe' };
  elements = stripe.elements({ appearance, clientSecret });
  const paymentElement = elements.create('payment', { layout: 'tabs' });
  paymentElement.mount('#payment-element');
}

3. Confirm Setup (Obtain Payment Method Token)

async function confirmPaymentMethod(){
  const { setupIntent, error } = await stripe.confirmSetup({
    elements,
    confirmParams: {},
    redirect: 'if_required'
  });
  if(error){
    console.error(error);
    throw error;
  }
  paymentMethodId = setupIntent.payment_method;
  return paymentMethodId;
}

4. Purchase (Stripe)

Call the Embrace Stripe purchase endpoint providing the payment method token and quote reference.

Endpoint reference: Purchase (Stripe) Endpoint

Front-End

async function completePurchase(){
  const body = {
    paymentMethodToken: paymentMethodId,
    quoteIdToPurchase: quoteId,
    analytics,
    allPetsVisitedVet,
    mailingAddress,
    billingAddress,
    agreeToTermsOfService,
    firstName,
    lastName
  };
  const resp = await fetch('/purchase-stripe', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body)
  });
  const data = await resp.json();
  if(data.purchaseSucceeded){
    console.log('Policy Number:', data.policyNumber);
  } else {
    console.error('Purchase error', data);
  }
}

Backend Example

app.MapPost("/purchase-stripe", async (HttpRequest request) =>
{
    using var reader = new StreamReader(request.Body, Encoding.UTF8);
    var rawBody = await reader.ReadToEndAsync();
    if (string.IsNullOrWhiteSpace(rawBody)) return Results.BadRequest(new { error = "Request body is empty" });

    string? quoteId = null;
    try
    {
        using var doc = JsonDocument.Parse(rawBody);
        if (doc.RootElement.TryGetProperty("quoteIdToPurchase", out var q)) quoteId = q.GetString();
    }
    catch { }

    if (string.IsNullOrWhiteSpace(quoteId)) return Results.BadRequest(new { error = "quoteIdToPurchase field is required" });

    var upstream = new HttpRequestMessage(HttpMethod.Post, $"https://[embrace-test-endpoint]/v2/quotes/fullquote/{quoteId}/purchase-stripe")
    {
        Content = new StringContent(rawBody, Encoding.UTF8, "application/json")
    };
    upstream.Headers.Add("epi-apim-subscription-key", embraceApiKey);

    var upstreamResp = await httpClient.SendAsync(upstream);
    var body = await upstreamResp.Content.ReadAsStringAsync();
    return Results.Content(body, "application/json", upstreamResp.StatusCode);
});

5. Success Handling

Successful purchase responses include policy identifiers:

{
  "purchaseSucceeded": true,
  "policyNumber": "INS-987654321"
}

Persist policyNumber and transition the user to a confirmation screen (avoid re-submission on refresh by clearing transient state).